home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / me39src1.arc / SEARCH.C < prev    next >
Text File  |  1987-07-14  |  34KB  |  1,430 lines

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions.  There are no special characters in the search
  4.  * strings.  Probably should have a regular expression search, or something
  5.  * like that.
  6.  *
  7.  * Aug. 1986 John M. Gamble:
  8.  *    Made forward and reverse search use the same scan routine.
  9.  *
  10.  *    Added a limited number of regular expressions - 'any',
  11.  *    'character class', 'closure', 'beginning of line', and
  12.  *    'end of line'.
  13.  *
  14.  *    Replacement metacharacters will have to wait for a re-write of
  15.  *    the replaces function, and a new variation of ldelete().
  16.  *
  17.  *    For those curious as to my references, i made use of
  18.  *    Kernighan & Plauger's "Software Tools."
  19.  *    I deliberately did not look at any published grep or editor
  20.  *    source (aside from this one) for inspiration.  I did make use of
  21.  *    Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
  22.  *    June, 1985 and modified them for the limited needs of character class
  23.  *    matching.  Any inefficiences, bugs, stupid coding examples, etc.,
  24.  *    are therefore my own responsibility.
  25.  *
  26.  * April 1987: John M. Gamble
  27.  *    Deleted the "if (n == 0) n = 1;" statements in front of the
  28.  *    search/hunt routines.  Since we now use a do loop, these
  29.  *    checks are unnecessary.  Consolidated common code into the
  30.  *    function delins().  Renamed global mclen matchlen,
  31.  *    and added the globals matchline, matchoff, patmatch, and
  32.  *    mlenold.
  33.  *    This gave us the ability to unreplace regular expression searches,
  34.  *    and to put the matched string into an evironment variable.
  35.  *    SOON TO COME: Meta-replacement characters!
  36.  *
  37.  *    25-apr-87    DML
  38.  *    - cleaned up an unneccessary if/else in forwsearch() and
  39.  *      backsearch()
  40.  *    - savematch() failed to malloc room for the terminating byte
  41.  *      of the match string (stomp...stomp...). It does now. Also
  42.  *      it now returns gracefully if malloc fails
  43.  *
  44.  *    July 1987: John M. Gamble
  45.  *    Set the variables matchlen and matchoff in the 'unreplace'
  46.  *    section of replaces().  It would confuse the function
  47.  *    savematch() if you replaced, unreplaced, then replaced
  48.  *    again (serves you right for being so wishy-washy...)
  49.  */
  50.  
  51. #include        <stdio.h>
  52. #include    "estruct.h"
  53. #include        "edef.h"
  54.  
  55. #if    LATTICE
  56. #define    void    int
  57. #endif
  58.  
  59. static int    amatch();
  60. static int    readpattern();
  61. static int    replaces();
  62. static int    nextch();
  63. static int    mcstr();
  64. static int    mceq();
  65. static int    cclmake();
  66. static int    biteq();
  67. static BITMAP   clearbits();
  68. static void     setbit();
  69.  
  70. /*
  71.  * forwsearch -- Search forward.  Get a search string from the user, and
  72.  *    search for the string.  If found, reset the "." to be just after
  73.  *    the match string, and (perhaps) repaint the display.
  74.  */
  75.  
  76. forwsearch(f, n)
  77. int f, n;    /* default flag / numeric argument */
  78. {
  79.     register int status = TRUE;
  80.  
  81.     /* If n is negative, search backwards.
  82.      * Otherwise proceed by asking for the search string.
  83.      */
  84.     if (n < 0)
  85.         return(backsearch(f, -n));
  86.  
  87.     /* Ask the user for the text of a pattern.  If the
  88.      * response is TRUE (responses other than FALSE are
  89.      * possible), search for the pattern for as long as
  90.      * n is positive (n == 0 will go through once, which
  91.      * is just fine).
  92.      */
  93.     if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE) {
  94.         do {
  95. #if    MAGIC
  96.             if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  97.                 status = mcscanner(&mcpat[0], FORWARD, PTEND);
  98.             else
  99. #endif
  100.                 status = scanner(&pat[0], FORWARD, PTEND);
  101.         } while ((--n > 0) && status);
  102.  
  103.         /* Save away the match, or complain
  104.          * if not there.
  105.          */
  106.         if (status == TRUE)
  107.             savematch();
  108.         else
  109.             mlwrite("Not found");
  110.     }
  111.     return(status);
  112. }
  113.  
  114. /*
  115.  * forwhunt -- Search forward for a previously acquired search string.
  116.  *    If found, reset the "." to be just after the match string,
  117.  *    and (perhaps) repaint the display.
  118.  */
  119.  
  120. forwhunt(f, n)
  121. int f, n;    /* default flag / numeric argument */
  122. {
  123.     register int status = TRUE;
  124.  
  125.     if (n < 0)        /* search backwards */
  126.         return(backhunt(f, -n));
  127.  
  128.     /* Make sure a pattern exists, or that we didn't switch
  129.      * into MAGIC mode until after we entered the pattern.
  130.      */
  131.     if (pat[0] == '\0')
  132.     {
  133.         mlwrite("No pattern set");
  134.         return FALSE;
  135.     }
  136. #if    MAGIC
  137.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  138.          mcpat[0].mc_type == MCNIL)
  139.     {
  140.         if (!mcstr())
  141.             return FALSE;
  142.     }
  143. #endif
  144.  
  145.     /* Search for the pattern for as long as
  146.      * n is positive (n == 0 will go through once, which
  147.      * is just fine).
  148.      */
  149.     do
  150.     {
  151. #if    MAGIC
  152.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  153.             status = mcscanner(&mcpat[0], FORWARD, PTEND);
  154.         else
  155. #endif
  156.             status = scanner(&pat[0], FORWARD, PTEND);
  157.     } while ((--n > 0) && status);
  158.  
  159.     /* Save away the match, or complain
  160.      * if not there.
  161.      */
  162.     if (status == TRUE)
  163.         savematch();
  164.     else
  165.         mlwrite("Not found");
  166.  
  167.     return(status);
  168. }
  169.  
  170. /*
  171.  * backsearch -- Reverse search.  Get a search string from the user, and
  172.  *    search, starting at "." and proceeding toward the front of the buffer.
  173.  *    If found "." is left pointing at the first character of the pattern
  174.  *    (the last character that was matched).
  175.  */
  176. backsearch(f, n)
  177. int f, n;    /* default flag / numeric argument */
  178. {
  179.     register int status = TRUE;
  180.  
  181.     /* If n is negative, search forwards.
  182.      * Otherwise proceed by asking for the search string.
  183.      */
  184.     if (n < 0)
  185.         return(forwsearch(f, -n));
  186.  
  187.     /* Ask the user for the text of a pattern.  If the
  188.      * response is TRUE (responses other than FALSE are
  189.      * possible), search for the pattern for as long as
  190.      * n is positive (n == 0 will go through once, which
  191.      * is just fine).
  192.      */
  193.     if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE) {
  194.         do {
  195. #if    MAGIC
  196.             if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  197.                 status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  198.             else
  199. #endif
  200.                 status = scanner(&tap[0], REVERSE, PTBEG);
  201.         } while ((--n > 0) && status);
  202.  
  203.         /* Save away the match, or complain
  204.          * if not there.
  205.          */
  206.         if (status == TRUE)
  207.             savematch();
  208.         else
  209.             mlwrite("Not found");
  210.     }
  211.     return(status);
  212. }
  213.  
  214. /*
  215.  * backhunt -- Reverse search for a previously acquired search string,
  216.  *    starting at "." and proceeding toward the front of the buffer.
  217.  *    If found "." is left pointing at the first character of the pattern
  218.  *    (the last character that was matched).
  219.  */
  220. backhunt(f, n)
  221.  
  222. int f, n;    /* default flag / numeric argument */
  223.  
  224. {
  225.     register int    status = TRUE;
  226.  
  227.     if (n < 0)
  228.         return(forwhunt(f, -n));
  229.  
  230.     /* Make sure a pattern exists, or that we didn't switch
  231.      * into MAGIC mode until after we entered the pattern.
  232.      */
  233.     if (tap[0] == '\0')
  234.     {
  235.         mlwrite("No pattern set");
  236.         return FALSE;
  237.     }
  238. #if    MAGIC
  239.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  240.          tapcm[0].mc_type == MCNIL)
  241.     {
  242.         if (!mcstr())
  243.             return FALSE;
  244.     }
  245. #endif
  246.  
  247.     /* Go search for it for as long as
  248.      * n is positive (n == 0 will go through once, which
  249.      * is just fine).
  250.      */
  251.     do
  252.     {
  253. #if    MAGIC
  254.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  255.             status = mcscanner(&tapcm[0], REVERSE, PTBEG);
  256.         else
  257. #endif
  258.             status = scanner(&tap[0], REVERSE, PTBEG);
  259.     } while ((--n > 0) && status);
  260.  
  261.     /* Save away the match, or complain
  262.      * if not there.
  263.      */
  264.     if (status == TRUE)
  265.         savematch();
  266.     else
  267.         mlwrite("Not found");
  268.  
  269.     return(status);
  270. }
  271.  
  272. #if    MAGIC
  273. /*
  274.  * mcscanner -- Search for a meta-pattern in either direction.  If found,
  275.  *    reset the "." to be at the start or just after the match string,
  276.  *    and (perhaps) repaint the display.
  277.  */
  278. int    mcscanner(mcpatrn, direct, beg_or_end)
  279. MC    *mcpatrn;        /* pointer into pattern */
  280. int    direct;        /* which way to go.*/
  281. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  282. {
  283.     LINE *curline;            /* current line during scan */
  284.     int curoff;            /* position within current line */
  285.  
  286.     /* If we are going in reverse, then the 'end' is actually
  287.      * the beginning of the pattern.  Toggle it.
  288.      */
  289.     beg_or_end ^= direct;
  290.  
  291.     /*
  292.      * Save the old matchlen length, in case it is
  293.      * horribly different (closure) from the old length.
  294.      * This is terribly important for query-replace undo
  295.      * command.
  296.      */
  297.     mlenold = matchlen;
  298.  
  299.     /* Setup local scan pointers to global ".".
  300.      */
  301.     curline = curwp->w_dotp;
  302.     curoff  = curwp->w_doto;
  303.  
  304.     /* Scan each character until we hit the head link record.
  305.      */
  306.     while (!boundry(curline, curoff, direct))
  307.     {
  308.         /* Save the current position in case we need to
  309.          * restore it on a match, and initialize matchlen to
  310.          * zero in case we are doing a search for replacement.
  311.          */
  312.         matchline = curline;
  313.         matchoff = curoff;
  314.         matchlen = 0;
  315.  
  316.         if (amatch(mcpatrn, direct, &curline, &curoff))
  317.         {
  318.             /* A SUCCESSFULL MATCH!!!
  319.              * reset the global "." pointers.
  320.              */
  321.             if (beg_or_end == PTEND)    /* at end of string */
  322.             {
  323.                 curwp->w_dotp = curline;
  324.                 curwp->w_doto = curoff;
  325.             }
  326.             else        /* at beginning of string */
  327.             {
  328.                 curwp->w_dotp = matchline;
  329.                 curwp->w_doto = matchoff;
  330.             }
  331.  
  332.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  333.             return TRUE;
  334.         }
  335.  
  336.         /* Advance the cursor.
  337.          */
  338.         nextch(&curline, &curoff, direct);
  339.     }
  340.  
  341.     return FALSE;    /* We could not find a match.*/
  342. }
  343.  
  344. /*
  345.  * amatch -- Search for a meta-pattern in either direction.  Based on the
  346.  *    recursive routine amatch() (for "anchored match") in
  347.  *    Kernighan & Plauger's "Software Tools".
  348.  */
  349. static int    amatch(mcptr, direct, pcwline, pcwoff)
  350. register MC    *mcptr;    /* string to scan for */
  351. int        direct;        /* which way to go.*/
  352. LINE        **pcwline;    /* current line during scan */
  353. int        *pcwoff;    /* position within current line */
  354. {
  355.     register int c;            /* character at current position */
  356.     LINE *curline;            /* current line during scan */
  357.     int curoff;            /* position within current line */
  358.     int nchars;
  359.  
  360.     /* Set up local scan pointers to ".", and get
  361.      * the current character.  Then loop around
  362.      * the pattern pointer until success or failure.
  363.      */
  364.     curline = *pcwline;
  365.     curoff = *pcwoff;
  366.  
  367.     /* The beginning-of-line and end-of-line metacharacters
  368.      * do not compare against characters, they compare
  369.      * against positions.
  370.      * BOL is guaranteed to be at the start of the pattern
  371.      * for forward searches, and at the end of the pattern
  372.      * for reverse searches.  The reverse is true for EOL.
  373.      * So, for a start, we check for them on entry.
  374.      */
  375.     if (mcptr->mc_type == BOL)
  376.     {
  377.         if (curoff != 0)
  378.             return FALSE;
  379.         mcptr++;
  380.     }
  381.  
  382.     if (mcptr->mc_type == EOL)
  383.     {
  384.         if (curoff != llength(curline))
  385.             return FALSE;
  386.         mcptr++;
  387.     }
  388.  
  389.     while (mcptr->mc_type != MCNIL)
  390.     {
  391.         c = nextch(&curline, &curoff, direct);
  392.  
  393.         if (mcptr->mc_type & CLOSURE)
  394.         {
  395.             /* Try to match as many characters as possible
  396.              * against the current meta-character.  A
  397.              * newline never matches a closure.
  398.              */
  399.             nchars = 0;
  400.             while (c != '\n' && mceq(c, mcptr))
  401.             {
  402.                 c = nextch(&curline, &curoff, direct);
  403.                 nchars++;
  404.             }
  405.  
  406.             /* We are now at the character that made us
  407.              * fail.  Try to match the rest of the pattern.
  408.              * Shrink the closure by one for each failure.
  409.              * Since closure matches *zero* or more occurences
  410.              * of a pattern, a match may start even if the
  411.              * previous loop matched no characters.
  412.              */
  413.             mcptr++;
  414.  
  415.             for (;;)
  416.             {
  417.                 c = nextch(&curline, &curoff, direct ^ REVERSE);
  418.  
  419.                 if (amatch(mcptr, direct, &curline, &curoff))
  420.                 {
  421.                     matchlen += nchars;
  422.                     goto success;
  423.                 }
  424.  
  425.                 if (nchars-- == 0)
  426.                     return FALSE;
  427.             }
  428.         }
  429.         else            /* Not closure.*/
  430.         {
  431.             /* The only way we'd get a BOL metacharacter
  432.              * at this point is at the end of the reversed pattern.
  433.              * The only way we'd get an EOL metacharacter
  434.              * here is at the end of a regular pattern.
  435.              * So if we match one or the other, and are at
  436.              * the appropriate position, we are guaranteed success
  437.              * (since the next pattern character has to be MCNIL).
  438.              * Before we report success, however, we back up by
  439.              * one character, so as to leave the cursor in the
  440.              * correct position.  For example, a search for ")$"
  441.              * will leave the cursor at the end of the line, while
  442.              * a search for ")<NL>" will leave the cursor at the
  443.              * beginning of the next line.  This follows the
  444.              * notion that the meta-character '$' (and likewise
  445.              * '^') match positions, not characters.
  446.              */
  447.             if (mcptr->mc_type == BOL)
  448.                 if (curoff == llength(curline))
  449.                 {
  450.                     c = nextch(&curline, &curoff,
  451.                            direct ^ REVERSE);
  452.                     goto success;
  453.                 }
  454.                 else
  455.                     return FALSE;
  456.  
  457.             if (mcptr->mc_type == EOL)
  458.                 if (curoff == 0)
  459.                 {
  460.                     c = nextch(&curline, &curoff,
  461.                            direct ^ REVERSE);
  462.                     goto success;
  463.                 }
  464.                 else
  465.                     return FALSE;
  466.  
  467.             /* Neither BOL nor EOL, so go through
  468.              * the meta-character equal function.
  469.              */
  470.             if (!mceq(c, mcptr))
  471.                 return FALSE;
  472.         }
  473.  
  474.         /* Increment the length counter and
  475.          * advance the pattern pointer.
  476.          */
  477.         matchlen++;
  478.         mcptr++;
  479.     }            /* End of mcptr loop.*/
  480.  
  481.     /* A SUCCESSFULL MATCH!!!
  482.      * Reset the "." pointers.
  483.      */
  484. success:
  485.     *pcwline = curline;
  486.     *pcwoff  = curoff;
  487.  
  488.     return TRUE;
  489. }
  490. #endif
  491.  
  492. /*
  493.  * scanner -- Search for a pattern in either direction.  If found,
  494.  *    reset the "." to be at the start or just after the match string,
  495.  *    and (perhaps) repaint the display.
  496.  */
  497. int    scanner(patrn, direct, beg_or_end)
  498. unsigned char *patrn;    /* string to scan for */
  499. int    direct;        /* which way to go.*/
  500. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  501. {
  502.     register int    c;        /* character at current position */
  503.     register unsigned char *patptr;    /* pointer into pattern */
  504.     LINE    *curline;        /* current line during scan */
  505.     int    curoff;            /* position within current line */
  506.     LINE    *scanline;        /* current line during scanning */
  507.     int    scanoff;        /* position in scanned line */
  508.  
  509.     /* If we are going in reverse, then the 'end' is actually
  510.      * the beginning of the pattern.  Toggle it.
  511.      */
  512.     beg_or_end ^= direct;
  513.  
  514.     /* Set up local pointers to global ".".
  515.      */
  516.     curline = curwp->w_dotp;
  517.     curoff = curwp->w_doto;
  518.  
  519.     /* Scan each character until we hit the head link record.
  520.      */
  521.     while (!boundry(curline, curoff, direct))
  522.     {
  523.         /* Save the current position in case we match
  524.          * the search string at this point.
  525.          */
  526.         matchline = curline;
  527.         matchoff = curoff;
  528.  
  529.         /* Get the character resolving newlines, and
  530.          * test it against first char in pattern.
  531.          */
  532.         c = nextch(&curline, &curoff, direct);
  533.  
  534.         if (eq(c, patrn[0]))    /* if we find it..*/
  535.         {
  536.             /* Setup scanning pointers.
  537.              */
  538.             scanline = curline;
  539.             scanoff = curoff;
  540.             patptr = &patrn[0];
  541.  
  542.             /* Scan through the pattern for a match.
  543.              */
  544.             while (*++patptr != '\0')
  545.             {
  546.                 c = nextch(&scanline, &scanoff, direct);
  547.  
  548.                 if (!eq(c, *patptr))
  549.                     goto fail;
  550.             }
  551.  
  552.             /* A SUCCESSFULL MATCH!!!
  553.              * reset the global "." pointers
  554.              */
  555.             if (beg_or_end == PTEND)    /* at end of string */
  556.             {
  557.                 curwp->w_dotp = scanline;
  558.                 curwp->w_doto = scanoff;
  559.             }
  560.             else        /* at beginning of string */
  561.             {
  562.                 curwp->w_dotp = matchline;
  563.                 curwp->w_doto = matchoff;
  564.             }
  565.  
  566.             curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
  567.             return TRUE;
  568.  
  569.         }
  570. fail:;            /* continue to search */
  571.     }
  572.  
  573.     return FALSE;    /* We could not find a match */
  574. }
  575.  
  576. /*
  577.  * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  578.  *    from the pattern.  If we are not in EXACT mode, fold out the case.
  579.  */
  580. int    eq(bc, pc)
  581. register int    bc;
  582. register int    pc;
  583. {
  584.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
  585.     {
  586.         if (islower(bc))
  587.             bc ^= DIFCASE;
  588.  
  589.         if (islower(pc))
  590.             pc ^= DIFCASE;
  591.     }
  592.  
  593.     return (bc == pc);
  594. }
  595.  
  596. /*
  597.  * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  598.  *    search string, create the reverse pattern and the magic
  599.  *    pattern, assuming we are in MAGIC mode (and defined that way).
  600.  *    Apat is not updated if the user types in an empty line.  If
  601.  *    the user typed an empty line, and there is no old pattern, it is
  602.  *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  603.  *    There is some do-it-yourself control expansion.  Change to using
  604.  *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  605.  *    string. 
  606.  */
  607. static int    readpattern(prompt, apat, srch)
  608. char    *prompt;
  609. char    apat[];
  610. int    srch;
  611. {
  612.     int status;
  613.     char tpat[NPAT+20];
  614.  
  615.     strcpy(tpat, prompt);    /* copy prompt to output string */
  616.     strcat(tpat, " [");    /* build new prompt string */
  617.     expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  618.     strcat(tpat, "]<META>: ");
  619.  
  620.     /* Read a pattern.  Either we get one,
  621.      * or we just get the META charater, and use the previous pattern.
  622.      * Then, if it's the search string, make a reversed pattern.
  623.      * *Then*, make the meta-pattern, if we are defined that way.
  624.      */
  625.     if ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE)
  626.     {
  627.         strcpy(apat, tpat);
  628.         if (srch)    /* If we are doing the search string.*/
  629.         {
  630.             mlenold = matchlen = strlen(apat);
  631.             /* Reverse string copy.
  632.              */
  633.             rvstrcpy(tap, apat);
  634. #if    MAGIC
  635.             /* Only make the meta-pattern if in magic mode,
  636.              * since the pattern in question might have an
  637.              * invalid meta combination.
  638.              */
  639.             if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
  640.                 mcclear();
  641.             else
  642.                 status = mcstr();
  643. #endif
  644.         }
  645.     }
  646.     else if (status == FALSE && apat[0] != 0)    /* Old one */
  647.         status = TRUE;
  648.  
  649.     return(status);
  650. }
  651.  
  652. /*
  653.  * savematch -- We found the pattern?  Let's save it away.
  654.  */
  655.  
  656. savematch()
  657. {
  658.     register char *ptr;    /* ptr into malloced last match string */
  659.     register int j;        /* index */
  660.     LINE *curline;        /* line of last match */
  661.     int curoff;        /* offset "      "    */
  662.  
  663.     /* free any existing match string */
  664.     if (patmatch != NULL)
  665.         free(patmatch);
  666.  
  667.     /* attempt to allocate a new one */
  668.     ptr = patmatch = malloc(matchlen + 1);
  669.     if (ptr == NULL)
  670.         return;
  671.  
  672.     /* save the match! */
  673.     curoff = matchoff;
  674.     curline = matchline;
  675.  
  676.     for (j = 0; j < matchlen; j++)
  677.         *ptr++ = nextch(&curline, &curoff, FORWARD);
  678.  
  679.     /* null terminate the match string */
  680.     *ptr = '\0';
  681. }
  682.  
  683. /*
  684.  * rvstrcpy -- Reverse string copy.
  685.  */
  686. rvstrcpy(rvstr, str)
  687. register char    *rvstr, *str;
  688. {
  689.     register int i;
  690.  
  691.     str += (i = strlen(str));
  692.  
  693.     while (i-- > 0)
  694.         *rvstr++ = *--str;
  695.  
  696.     *rvstr = '\0';
  697. }
  698.  
  699. /*
  700.  * sreplace -- Search and replace.
  701.  */
  702. sreplace(f, n)
  703.  
  704. int f;        /* default flag */
  705. int n;        /* # of repetitions wanted */
  706.  
  707. {
  708.     return(replaces(FALSE, f, n));
  709. }
  710.  
  711. /*
  712.  * qreplace -- search and replace with query.
  713.  */
  714. qreplace(f, n)
  715. int f;        /* default flag */
  716. int n;        /* # of repetitions wanted */
  717. {
  718.     return(replaces(TRUE, f, n));
  719. }
  720.  
  721. /*
  722.  * replaces -- Search for a string and replace it with another
  723.  *    string.  Query might be enabled (according to kind).
  724.  */
  725. static int    replaces(kind, f, n)
  726. int    kind;    /* Query enabled flag */
  727. int    f;    /* default flag */
  728. int    n;    /* # of repetitions wanted */
  729. {
  730.     register int status;    /* success flag on pattern inputs */
  731.     register int rlength;    /* length of replacement string */
  732.     register int numsub;    /* number of substitutions */
  733.     register int nummatch;    /* number of found matches */
  734.     int nlflag;        /* last char of search string a <NL>? */
  735.     int nlrepl;        /* was a replace done on the last line? */
  736.     char c;            /* input char for query */
  737.     char tpat[NPAT];    /* temporary to hold search pattern */
  738.     LINE *origline;        /* original "." position */
  739.     int origoff;        /* and offset (for . query option) */
  740.     LINE *lastline;        /* position of last replace and */
  741.     int lastoff;        /* offset (for 'u' query option) */
  742.  
  743.     if (curbp->b_mode & MDVIEW)    /* don't allow this command if    */
  744.         return(rdonly());    /* we are in read only mode    */
  745.  
  746.     /* Check for negative repetitions.
  747.      */
  748.     if (f && n < 0)
  749.         return(FALSE);
  750.  
  751.     /* Ask the user for the text of a pattern.
  752.      */
  753.     if ((status = readpattern(
  754.         (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE))
  755.                                 != TRUE)
  756.         return(status);
  757.  
  758.     /* Ask for the replacement string.
  759.      */
  760.     if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT)
  761.         return(status);
  762.  
  763.     /* Find the length of the replacement string.
  764.      */
  765.     rlength = strlen(&rpat[0]);
  766.  
  767.     /* Set up flags so we can make sure not to do a recursive
  768.      * replace on the last line.
  769.      */
  770.     nlflag = (pat[matchlen - 1] == '\n');
  771.     nlrepl = FALSE;
  772.  
  773.     if (kind)
  774.     {
  775.         /* Build query replace question string.
  776.          */
  777.         strcpy(tpat, "Replace '");
  778.         expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  779.         strcat(tpat, "' with '");
  780.         expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  781.         strcat(tpat, "'? ");
  782.  
  783.         /* Initialize last replaced pointers.
  784.          */
  785.         lastline = NULL;
  786.         lastoff = 0;
  787.     }
  788.  
  789.     /* Save original . position, init the number of matches and
  790.      * substitutions, and scan through the file.
  791.      */
  792.     origline = curwp->w_dotp;
  793.     origoff = curwp->w_doto;
  794.     numsub = 0;
  795.     nummatch = 0;
  796.  
  797.     while ( (f == FALSE || n > nummatch) &&
  798.         (nlflag == FALSE || nlrepl == FALSE) )
  799.     {
  800.         /* Search for the pattern.
  801.          * If we search with a regular expression,
  802.          * matchlen is reset to the true length of
  803.          * the matched string.
  804.          */
  805. #if    MAGIC
  806.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  807.         {
  808.             if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
  809.                 break;
  810.         }
  811.         else
  812. #endif
  813.             if (!scanner(&pat[0], FORWARD, PTBEG))
  814.                 break;        /* all done */
  815.  
  816.         ++nummatch;    /* Increment # of matches */
  817.  
  818.         /* Check if we are on the last line.
  819.          */
  820.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  821.  
  822.         /* Check for query.
  823.          */
  824.         if (kind)
  825.         {
  826.             /* Get the query.
  827.              */
  828. pprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  829. qprompt:
  830.             update(TRUE);  /* show the proposed place to change */
  831.             c = tgetc();            /* and input */
  832.             mlwrite("");            /* and clear it */
  833.  
  834.             /* And respond appropriately.
  835.              */
  836.             switch (c)
  837.             {
  838.                 case 'y':    /* yes, substitute */
  839.                 case ' ':
  840.                     savematch();
  841.                     break;
  842.  
  843.                 case 'n':    /* no, onword */
  844.                     forwchar(FALSE, 1);
  845.                     continue;
  846.  
  847.                 case '!':    /* yes/stop asking */
  848.                     kind = FALSE;
  849.                     break;
  850.  
  851.                 case 'u':    /* undo last and re-prompt */
  852.  
  853.                     /* Restore old position.
  854.                      */
  855.                     if (lastline == NULL)
  856.                     {
  857.                         /* There is nothing to undo.
  858.                          */
  859.                         TTbeep();
  860.                         goto pprompt;
  861.                     }
  862.                     curwp->w_dotp = lastline;
  863.                     curwp->w_doto = lastoff;
  864.                     lastline = NULL;
  865.                     lastoff = 0;
  866.  
  867.                     /* Delete the new string.
  868.                      */
  869.                     backchar(FALSE, rlength);
  870.                     status = delins(rlength, patmatch);
  871.                     if (status != TRUE)
  872.                         return (status);
  873.  
  874.                     /* Record one less substitution,
  875.                      * backup, and reprompt.
  876.                      */
  877.                     --numsub;
  878.                     backchar(FALSE, mlenold);
  879.                     matchline = curwp->w_dotp;
  880.                     matchoff  = curwp->w_doto;
  881.                     goto pprompt;
  882.  
  883.                 case '.':    /* abort! and return */
  884.                     /* restore old position */
  885.                     curwp->w_dotp = origline;
  886.                     curwp->w_doto = origoff;
  887.                     curwp->w_flag |= WFMOVE;
  888.  
  889.                 case BELL:    /* abort! and stay */
  890.                     mlwrite("Aborted!");
  891.                     return(FALSE);
  892.  
  893.                 default:    /* bitch and beep */
  894.                     TTbeep();
  895.  
  896.                 case '?':    /* help me */
  897.                     mlwrite(
  898. "(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
  899.                     goto qprompt;
  900.  
  901.             }    /* end of switch */
  902.         }    /* end of "if kind" */
  903.  
  904.         /*
  905.          * Delete the sucker, and insert its
  906.          * replacement.
  907.          */
  908.         status = delins(matchlen, &rpat[0]);
  909.         if (status != TRUE)
  910.             return (status);
  911.  
  912.         /* Save where we are if we might undo this....
  913.          */
  914.         if (kind)
  915.         {
  916.             lastline = curwp->w_dotp;
  917.             lastoff = curwp->w_doto;
  918.         }
  919.  
  920.         numsub++;    /* increment # of substitutions */
  921.     }
  922.  
  923.     /* And report the results.
  924.      */
  925.     mlwrite("%d substitutions", numsub);
  926.     return(TRUE);
  927. }
  928.  
  929. /*
  930.  * delins -- Delete a specified length from the current
  931.  *    point, then insert the string.
  932.  */
  933. delins(dlength, instr)
  934. int    dlength;
  935. char    *instr;
  936. {
  937.     int    status;
  938.     char    tmpc;
  939.  
  940.     /* Zap what we gotta,
  941.      * and insert its replacement.
  942.      */
  943.     if (!(status = ldelete((long) dlength, FALSE)))
  944.     {
  945.         mlwrite("%%ERROR while deleting");
  946.         return(FALSE);
  947.     }
  948.     else
  949.         while (tmpc = *instr)
  950.         {
  951.             status = (tmpc == '\n'? lnewline(): linsert(1, tmpc));
  952.  
  953.             /* Insertion error?
  954.              */
  955.             if (!status)
  956.             {
  957.                 mlwrite("%%Out of memory while inserting");
  958.                 break;
  959.             }
  960.             instr++;
  961.         }
  962.     return (status);
  963. }
  964.  
  965. /*
  966.  * expandp -- Expand control key sequences for output.
  967.  */
  968. expandp(srcstr, deststr, maxlength)
  969. char *srcstr;    /* string to expand */
  970. char *deststr;    /* destination of expanded string */
  971. int maxlength;    /* maximum chars in destination */
  972. {
  973.     unsigned char c;    /* current char to translate */
  974.  
  975.     /* Scan through the string.
  976.      */
  977.     while ((c = *srcstr++) != 0)
  978.     {
  979.         if (c == '\n')        /* it's a newline */
  980.         {
  981.             *deststr++ = '<';
  982.             *deststr++ = 'N';
  983.             *deststr++ = 'L';
  984.             *deststr++ = '>';
  985.             maxlength -= 4;
  986.         }
  987.         else if (c < 0x20 || c == 0x7f)    /* control character */
  988.         {
  989.             *deststr++ = '^';
  990.             *deststr++ = c ^ 0x40;
  991.             maxlength -= 2;
  992.         }
  993.         else if (c == '%')
  994.         {
  995.             *deststr++ = '%';
  996.             *deststr++ = '%';
  997.             maxlength -= 2;
  998.         }
  999.         else            /* any other character */
  1000.         {
  1001.             *deststr++ = c;
  1002.             maxlength--;
  1003.         }
  1004.  
  1005.         /* check for maxlength */
  1006.         if (maxlength < 4)
  1007.         {
  1008.             *deststr++ = '$';
  1009.             *deststr = '\0';
  1010.             return(FALSE);
  1011.         }
  1012.     }
  1013.     *deststr = '\0';
  1014.     return(TRUE);
  1015. }
  1016.  
  1017. /*
  1018.  * boundry -- Return information depending on whether we may search no
  1019.  *    further.  Beginning of file and end of file are the obvious
  1020.  *    cases, but we may want to add further optional boundry restrictions
  1021.  *    in future, a' la VMS EDT.  At the moment, just return TRUE or
  1022.  *    FALSE depending on if a boundry is hit (ouch).
  1023.  */
  1024. int    boundry(curline, curoff, dir)
  1025. LINE    *curline;
  1026. int    curoff, dir;
  1027. {
  1028.     register int    border;
  1029.  
  1030.     if (dir == FORWARD)
  1031.     {
  1032.         border = (curoff == llength(curline)) &&
  1033.              (lforw(curline) == curbp->b_linep);
  1034.     }
  1035.     else
  1036.     {
  1037.         border = (curoff == 0) &&
  1038.              (lback(curline) == curbp->b_linep);
  1039.     }
  1040.     return (border);
  1041. }
  1042.  
  1043. /*
  1044.  * nextch -- retrieve the next/previous character in the buffer,
  1045.  *    and advance/retreat the point.
  1046.  *    The order in which this is done is significant, and depends
  1047.  *    upon the direction of the search.  Forward searches look at
  1048.  *    the current character and move, reverse searches move and
  1049.  *    look at the character.
  1050.  */
  1051. static int nextch(pcurline, pcuroff, dir)
  1052. LINE    **pcurline;
  1053. int    *pcuroff;
  1054. int    dir;
  1055. {
  1056.     register LINE    *curline;
  1057.     register int    curoff;
  1058.     register int    c;
  1059.  
  1060.     curline = *pcurline;
  1061.     curoff = *pcuroff;
  1062.  
  1063.     if (dir == FORWARD)
  1064.     {
  1065.         if (curoff == llength(curline))        /* if at EOL */
  1066.         {
  1067.             curline = lforw(curline);    /* skip to next line */
  1068.             curoff = 0;
  1069.             c = '\n';            /* and return a <NL> */
  1070.         }
  1071.         else
  1072.             c = lgetc(curline, curoff++);    /* get the char */
  1073.     }
  1074.     else            /* Reverse.*/
  1075.     {
  1076.         if (curoff == 0)
  1077.         {
  1078.             curline = lback(curline);
  1079.             curoff = llength(curline);
  1080.             c = '\n';
  1081.         }
  1082.         else
  1083.             c = lgetc(curline, --curoff);
  1084.  
  1085.     }
  1086.     *pcurline = curline;
  1087.     *pcuroff = curoff;
  1088.  
  1089.     return (c);
  1090. }
  1091.  
  1092. #if    MAGIC
  1093. /*
  1094.  * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  1095.  *    a literal character when (1) it is the first character in the
  1096.  *    pattern, and (2) when preceded by a symbol that does not allow
  1097.  *    closure, such as a newline, beginning of line symbol, or another
  1098.  *    closure symbol.
  1099.  *
  1100.  *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  1101.  *    speaking, unnecessary.  But right now we are so cramped for
  1102.  *    code space that i will grab what i can in order to remain
  1103.  *    within the 64K limit.  C compilers actually do very little
  1104.  *    in the way of optimizing - they expect you to do that.
  1105.  */
  1106. static int mcstr()
  1107. {
  1108.     MC    *mcptr, *rtpcm;
  1109.     char    *patptr;
  1110.      int    mj;
  1111.      int    pchr;
  1112.      int    status = TRUE;
  1113.      int    does_closure = FALSE;
  1114.  
  1115.     /* If we had metacharacters in the MC array previously,
  1116.      * free up any bitmaps that may have been allocated.
  1117.      */
  1118.     if (magical)
  1119.         mcclear();
  1120.  
  1121.     magical = FALSE;
  1122.     mj = 0;
  1123.     mcptr = &mcpat[0];
  1124.     patptr = &pat[0];
  1125.  
  1126.     while ((pchr = *patptr) && status)
  1127.     {
  1128.         switch (pchr)
  1129.         {
  1130.             case MC_CCL:
  1131.                 status = cclmake(&patptr, mcptr);
  1132.                 magical = TRUE;
  1133.                 does_closure = TRUE;
  1134.                 break;
  1135.             case MC_BOL:
  1136.                 if (mj != 0)
  1137.                     goto litcase;
  1138.  
  1139.                 mcptr->mc_type = BOL;
  1140.                 magical = TRUE;
  1141.                 does_closure = FALSE;
  1142.                 break;
  1143.             case MC_EOL:
  1144.                 if (*(patptr + 1) != '\0')
  1145.                     goto litcase;
  1146.  
  1147.                 mcptr->mc_type = EOL;
  1148.                 magical = TRUE;
  1149.                 does_closure = FALSE;
  1150.                 break;
  1151.             case MC_ANY:
  1152.                 mcptr->mc_type = ANY;
  1153.                 magical = TRUE;
  1154.                 does_closure = TRUE;
  1155.                 break;
  1156.             case MC_CLOSURE:
  1157.                 /* Does the closure symbol mean closure here?
  1158.                  * If so, back up to the previous element
  1159.                  * and indicate it is enclosed.
  1160.                  */
  1161.                 if (!does_closure)
  1162.                     goto litcase;
  1163.                 mj--;
  1164.                 mcptr--;
  1165.                 mcptr->mc_type |= CLOSURE;
  1166.                 magical = TRUE;
  1167.                 does_closure = FALSE;
  1168.                 break;
  1169.  
  1170.             /* Note: no break between MC_ESC case and the default.
  1171.              */
  1172.             case MC_ESC:
  1173.                 if (*(patptr + 1) != '\0')
  1174.                 {
  1175.                     pchr = *++patptr;
  1176.                     magical = TRUE;
  1177.                 }
  1178.             default:
  1179. litcase:            mcptr->mc_type = LITCHAR;
  1180.                 mcptr->u.lchar = pchr;
  1181.                 does_closure = (pchr != '\n');
  1182.                 break;
  1183.         }        /* End of switch.*/
  1184.         mcptr++;
  1185.         patptr++;
  1186.         mj++;
  1187.     }        /* End of while.*/
  1188.  
  1189.     /* Close off the meta-string.
  1190.      */
  1191.     mcptr->mc_type = MCNIL;
  1192.  
  1193.     /* Set up the reverse array, if the status is good.  Please note the
  1194.      * structure assignment - your compiler may not like that.
  1195.      * If the status is not good, nil out the meta-pattern.
  1196.      * The only way the status would be bad is from the cclmake()
  1197.      * routine, and the bitmap for that member is guarenteed to be
  1198.      * freed.  So we stomp a MCNIL value there, and call mcclear()
  1199.      * to free any other bitmaps.
  1200.      */
  1201.     if (status)
  1202.     {
  1203.         rtpcm = &tapcm[0];
  1204.         while (--mj >= 0)
  1205.         {
  1206. #if    LATTICE
  1207.             movmem(--mcptr, rtpcm++, sizeof (MC));
  1208. #endif
  1209.  
  1210. #if    MWC86 | AZTEC | MSC | TURBO | VMS | USG | BSD | V7
  1211.             *rtpcm++ = *--mcptr;
  1212. #endif
  1213.         }
  1214.         rtpcm->mc_type = MCNIL;
  1215.     }
  1216.     else
  1217.     {
  1218.         (--mcptr)->mc_type = MCNIL;
  1219.         mcclear();
  1220.     }
  1221.  
  1222.     return(status);
  1223. }
  1224.  
  1225. /*
  1226.  * mcclear -- Free up any CCL bitmaps, and MCNIL the MC arrays.
  1227.  */
  1228. mcclear()
  1229. {
  1230.     register MC    *mcptr;
  1231.  
  1232.     mcptr = &mcpat[0];
  1233.  
  1234.     while (mcptr->mc_type != MCNIL)
  1235.     {
  1236.         if ((mcptr->mc_type & MASKCL) == CCL ||
  1237.             (mcptr->mc_type & MASKCL) == NCCL)
  1238.             free(mcptr->u.cclmap);
  1239.         mcptr++;
  1240.     }
  1241.     mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1242. }
  1243.  
  1244. /*
  1245.  * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1246.  *    Software Tools, this is the function omatch(), but i felt there
  1247.  *    were too many functions with the 'match' name already.
  1248.  */
  1249. static int    mceq(bc, mt)
  1250. int    bc;
  1251. MC    *mt;
  1252. {
  1253.     register int result;
  1254.  
  1255.     switch (mt->mc_type & MASKCL)
  1256.     {
  1257.         case LITCHAR:
  1258.             result = eq(bc, mt->u.lchar);
  1259.             break;
  1260.  
  1261.         case ANY:
  1262.             result = (bc != '\n');
  1263.             break;
  1264.  
  1265.         case CCL:
  1266.             if (!(result = biteq(bc, mt->u.cclmap)))
  1267.             {
  1268.                 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1269.                     (isletter(bc)))
  1270.                 {
  1271.                     result = biteq(CHCASE(bc), mt->u.cclmap);
  1272.                 }
  1273.             }
  1274.             break;
  1275.  
  1276.         case NCCL:
  1277.             result = !biteq(bc, mt->u.cclmap);
  1278.  
  1279.             if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1280.                 (isletter(bc)))
  1281.             {
  1282.                 result &= !biteq(CHCASE(bc), mt->u.cclmap);
  1283.             }
  1284.             break;
  1285.  
  1286.         default:
  1287.             mlwrite("mceq: what is %d?", mt->mc_type);
  1288.             result = FALSE;
  1289.             break;
  1290.  
  1291.     }    /* End of switch.*/
  1292.  
  1293.     return (result);
  1294. }
  1295.  
  1296. /*
  1297.  * cclmake -- create the bitmap for the character class.
  1298.  *    ppatptr is left pointing to the end-of-character-class character,
  1299.  *    so that a loop may automatically increment with safety.
  1300.  */
  1301. static int    cclmake(ppatptr, mcptr)
  1302. char    **ppatptr;
  1303. MC    *mcptr;
  1304. {
  1305.     BITMAP        clearbits();
  1306.     BITMAP        bmap;
  1307.     register char    *patptr;
  1308.     register int    pchr, ochr;
  1309.  
  1310.     if ((bmap = clearbits()) == NULL)
  1311.     {
  1312.         mlwrite("%%Out of memory");
  1313.         return FALSE;
  1314.     }
  1315.  
  1316.     mcptr->u.cclmap = bmap;
  1317.     patptr = *ppatptr;
  1318.  
  1319.     /*
  1320.      * Test the initial character(s) in ccl for
  1321.      * special cases - negate ccl, or an end ccl
  1322.      * character as a first character.  Anything
  1323.      * else gets set in the bitmap.
  1324.      */
  1325.     if (*++patptr == MC_NCCL)
  1326.     {
  1327.         patptr++;
  1328.         mcptr->mc_type = NCCL;
  1329.     }
  1330.     else
  1331.         mcptr->mc_type = CCL;
  1332.  
  1333.     if ((ochr = *patptr) == MC_ECCL)
  1334.     {
  1335.         mlwrite("%%No characters in character class");
  1336.         return (FALSE);
  1337.     }
  1338.     else
  1339.     {
  1340.         if (ochr == MC_ESC)
  1341.             ochr = *++patptr;
  1342.  
  1343.         setbit(ochr, bmap);
  1344.         patptr++;
  1345.     }
  1346.  
  1347.     while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
  1348.     {
  1349.         switch (pchr)
  1350.         {
  1351.             /* Range character loses its meaning
  1352.              * if it is the last character in
  1353.              * the class.
  1354.              */
  1355.             case MC_RCCL:
  1356.                 if (*(patptr + 1) == MC_ECCL)
  1357.                     setbit(pchr, bmap);
  1358.                 else
  1359.                 {
  1360.                     pchr = *++patptr;
  1361.                     while (++ochr <= pchr)
  1362.                         setbit(ochr, bmap);
  1363.                 }
  1364.                 break;
  1365.  
  1366.             /* Note: no break between case MC_ESC and the default.
  1367.              */
  1368.             case MC_ESC:
  1369.                 pchr = *++patptr;
  1370.             default:
  1371.                 setbit(pchr, bmap);
  1372.                 break;
  1373.         }
  1374.         patptr++;
  1375.         ochr = pchr;
  1376.     }
  1377.  
  1378.     *ppatptr = patptr;
  1379.  
  1380.     if (ochr == '\0')
  1381.     {
  1382.         mlwrite("%%Character class not ended");
  1383.         free(bmap);
  1384.         return FALSE;
  1385.     }
  1386.     return TRUE;
  1387. }
  1388.  
  1389. /*
  1390.  * biteq -- is the character in the bitmap?
  1391.  */
  1392. static int    biteq(bc, cclmap)
  1393. int    bc;
  1394. BITMAP    cclmap;
  1395. {
  1396.     if (bc >= HICHAR)
  1397.         return FALSE;
  1398.  
  1399.     return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1400. }
  1401.  
  1402. /*
  1403.  * clearbits -- Allocate and zero out a CCL bitmap.
  1404.  */
  1405. static    BITMAP clearbits()
  1406. {
  1407.     char        *malloc();
  1408.  
  1409.     BITMAP        cclstart, cclmap;
  1410.     register int    j;
  1411.  
  1412.     if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
  1413.         for (j = 0; j < HIBYTE; j++)
  1414.             *cclmap++ = 0;
  1415.  
  1416.     return (cclstart);
  1417. }
  1418.  
  1419. /*
  1420.  * setbit -- Set a bit (ON only) in the bitmap.
  1421.  */
  1422. static void setbit(bc, cclmap)
  1423. int    bc;
  1424. BITMAP    cclmap;
  1425. {
  1426.     if (bc < HICHAR)
  1427.         *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1428. }
  1429. #endif
  1430.